/*
 * Copyright (C) 2010 Ryan Morton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.games.rm.patternpuzzler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class GameBoard4 extends Activity implements OnTouchListener, OnClickListener, CountdownTimerFinishedListener
{
	public static final float LEFT_VOLUME = 4f, RIGHT_VOLUME = LEFT_VOLUME;
	
	//main layouts**********/
	private RelativeLayout redButtonLayout;
	private RelativeLayout yellowButtonLayout;
	private RelativeLayout blueButtonLayout;
	private RelativeLayout greenButtonLayout;
	private LinearLayout statusbarLayout;
	private LinearLayout pauseMenuLayout;
	private LinearLayout successLayout;
	private LinearLayout failLayout;
	private TextView countdownTimerText;
	
	//buttons*************/
	private ImageView redButtonTop;
	private ImageView redButtonLight;
	private ImageView yellowButtonTop;
	private ImageView yellowButtonLight;
	private ImageView blueButtonTop;
	private ImageView blueButtonLight;
	private ImageView greenButtonTop;
	private ImageView greenButtonLight;
	private ImageView forwardButton;
	private ImageView replayButton;
	private ImageView pauseButton;
	private TextView resumeButton;
	private RadioGroup fxRadioGroup;
	private RadioButton fxOnRadioButton;
	private RadioButton fxOffRadioButton;
	
	//other things***********/
	private TextView levelTextBox;
	private boolean buttonsActive;
	private boolean statusbarActive;
	private boolean showingPattern;
	private boolean isGameOver;
	private boolean replayResetUsed;
	private boolean soundFxOn;
	private Animation[] animationCache;
	private CountdownTimer myCountdownTimer;
	private HashMap<Integer, ImageView> lightedButtonMap;
	private ArrayList<Integer> myPattern;
	private int currentPatternIndex; //this stores the current index of the pattern ... to check against user input
	private Random randomGen;
	private PatternPlayer myPatternPlayer;
	private Savegame mySaveGame;
	private HashMap<ImageView,MediaPlayer> soundCache;
	HashMap<ImageView, MediaPlayer> patternPlayerSoundCache;
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		//System.out.println("onCreate");
		super.onCreate(savedInstanceState);
		//set up the window properties
        Utilities.setupActivityWindow(this);
        
        //inflate the main layout
        this.setContentView(R.layout.game_board_4);
        
        //run the init();
        this.init();
        
        //go to onResume for what happens next
	}
	
	/***
     * initialize the variables
     */
	private void init()
	{
		//init animation cache
		animationCache = new Animation[2];
		animationCache[0] = AnimationUtils.loadAnimation(this, R.anim.fade_in_buttons);
		animationCache[1] = AnimationUtils.loadAnimation(this, R.anim.fade_in_200);
		
		//init layouts************/
		redButtonLayout = (RelativeLayout)this.findViewById(R.id.GameBoard_4_Red_Button_Layout);
		yellowButtonLayout = (RelativeLayout)this.findViewById(R.id.GameBoard_4_Yellow_Button_Layout);
		blueButtonLayout = (RelativeLayout)this.findViewById(R.id.GameBoard_4_Blue_Button_Layout);
		greenButtonLayout = (RelativeLayout)this.findViewById(R.id.GameBoard_4_Green_Button_Layout);
		statusbarLayout = (LinearLayout)this.findViewById(R.id.GameBoard_4_StatusBar_Layout);
		pauseMenuLayout = (LinearLayout)this.findViewById(R.id.GameBoard_4_Pause_Menu_Layout);
		successLayout = (LinearLayout)this.findViewById(R.id.GameBoard_4_Success_Layout);
		failLayout = (LinearLayout)this.findViewById(R.id.GameBoard_4_Fail_Layout);
		countdownTimerText = (TextView)this.findViewById(R.id.GameBoard_4_Countdown);
		
		//init buttons**************/
		redButtonTop = (ImageView)redButtonLayout.findViewById(R.id.GameBoard_4_Red_Button);
		redButtonLight = (ImageView)redButtonLayout.findViewById(R.id.GameBoard_4_Red_Button_On);
		yellowButtonTop = (ImageView)yellowButtonLayout.findViewById(R.id.GameBoard_4_Yellow_Button);
		yellowButtonLight = (ImageView)yellowButtonLayout.findViewById(R.id.GameBoard_4_Yellow_Button_On);
		blueButtonTop = (ImageView)blueButtonLayout.findViewById(R.id.GameBoard_4_Blue_Button);
		blueButtonLight = (ImageView)blueButtonLayout.findViewById(R.id.GameBoard_4_Blue_Button_On);
		greenButtonTop = (ImageView)greenButtonLayout.findViewById(R.id.GameBoard_4_Green_Button);
		greenButtonLight = (ImageView)greenButtonLayout.findViewById(R.id.GameBoard_4_Green_Button_On);
		forwardButton = (ImageView)statusbarLayout.findViewById(R.id.GameBoard_4_StatusBar_Forward_Button);
		replayButton = (ImageView)statusbarLayout.findViewById(R.id.GameBoard_4_StatusBar_Replay_Button);
		pauseButton = (ImageView)statusbarLayout.findViewById(R.id.GameBoard_4_StatusBar_Pause_Button);
		resumeButton = (TextView)pauseMenuLayout.findViewById(R.id.GameBoard_4_Pause_Menu_Resume_Game_Button);
		fxRadioGroup = (RadioGroup)this.findViewById(R.id.GameBoard_4_Pause_Menu_FxGroup);
		fxOffRadioButton = (RadioButton)fxRadioGroup.findViewById(R.id.GameBoard_4_Pause_Menu_Fx_Off);
		fxOnRadioButton = (RadioButton)fxRadioGroup.findViewById(R.id.GameBoard_4_Pause_Menu_Fx_On);
		/*according to the Android JAVA Doc, the code below increases performance:
		"Setting a solid background color for the drawing cache's bitmaps will improve performance and memory usage.
		Note, though that this should only be used if this view will always be drawn on top of a solid color."
		*/
		redButtonLight.setDrawingCacheBackgroundColor(Color.BLACK);
		yellowButtonLight.setDrawingCacheBackgroundColor(Color.BLACK);
		blueButtonLight.setDrawingCacheBackgroundColor(Color.BLACK);
		greenButtonLight.setDrawingCacheBackgroundColor(Color.BLACK);
		//set onTouch/onClick listeners
		redButtonTop.setOnTouchListener(this);
		yellowButtonTop.setOnTouchListener(this);
		blueButtonTop.setOnTouchListener(this);
		greenButtonTop.setOnTouchListener(this);
		forwardButton.setOnClickListener(this);
		replayButton.setOnClickListener(this);
		pauseButton.setOnClickListener(this);
		resumeButton.setOnClickListener(this);
		fxOffRadioButton.setOnClickListener(this);
		fxOnRadioButton.setOnClickListener(this);
		
		//init other things****************/
		levelTextBox = (TextView)statusbarLayout.findViewById(R.id.GameBoard_4_StatusBar_Level_Text);
		buttonsActive = false;
		statusbarActive = false;
		showingPattern = false;
		isGameOver = false;
		replayResetUsed = false;
		soundFxOn = Utilities.loadSettings(this).isSoundOn();
		randomGen = new Random(System.currentTimeMillis());
		lightedButtonMap = new HashMap<Integer, ImageView>(4);
		lightedButtonMap.put(1, redButtonLight);
		lightedButtonMap.put(2, yellowButtonLight);
		lightedButtonMap.put(3, blueButtonLight);
		lightedButtonMap.put(4, greenButtonLight);
		//create gameboard sound caches... one for this, and one for the pattern player
		soundCache = new HashMap<ImageView, MediaPlayer>(4);
		patternPlayerSoundCache = new HashMap<ImageView, MediaPlayer>(4);
		MediaPlayer currentMP = MediaPlayer.create(this, R.raw.fx_430);
		currentMP.setLooping(true);
		currentMP.setVolume(LEFT_VOLUME,RIGHT_VOLUME);
		soundCache.put(redButtonTop, currentMP);
		patternPlayerSoundCache.put(redButtonLight, currentMP);
		currentMP = MediaPlayer.create(this, R.raw.fx_530);
		currentMP.setLooping(true);
		currentMP.setVolume(LEFT_VOLUME,RIGHT_VOLUME);
		soundCache.put(yellowButtonTop, currentMP);
		patternPlayerSoundCache.put(yellowButtonLight, currentMP);
		currentMP = MediaPlayer.create(this, R.raw.fx_630);
		currentMP.setLooping(true);
		currentMP.setVolume(LEFT_VOLUME,RIGHT_VOLUME);
		soundCache.put(blueButtonTop, currentMP);
		patternPlayerSoundCache.put(blueButtonLight, currentMP);
		currentMP = MediaPlayer.create(this, R.raw.fx_730);
		currentMP.setLooping(true);
		currentMP.setVolume(LEFT_VOLUME,RIGHT_VOLUME);
		soundCache.put(greenButtonTop, currentMP);
		patternPlayerSoundCache.put(greenButtonLight, currentMP);
		myCountdownTimer = new CountdownTimer(countdownTimerText, new Handler());
		//buttonLighter = new Handler();
		myPatternPlayer = new PatternPlayer(Utilities.loadSettings(this).shouldSpeedIncrease(), 
				lightedButtonMap,patternPlayerSoundCache, this, new Handler());
		myPattern = myPatternPlayer.getPattern();
		myCountdownTimer.setTimerListener(this);
	}
	/***
	 * this is only called when the activity is first initialized/created
	 */
	private void startFirstRun()
	{
		//start button animations
		redButtonLayout.startAnimation(animationCache[0]);
		yellowButtonLayout.startAnimation(animationCache[0]);
		blueButtonLayout.startAnimation(animationCache[0]);
		greenButtonLayout.startAnimation(animationCache[0]);
		
		//start the countdown timer... delay execution for 1 sec.
		new Handler().postDelayed(new Runnable() {
			
			@Override
			public void run()
			{
				GameBoard4.this.countdownTimerText.setVisibility(View.VISIBLE);
				GameBoard4.this.myCountdownTimer.start();
			}
		}, 1000);
	}
	
	/***
	 * this starts the game after a savegame is loaded
	 */
	private void startFromSavedGame(boolean wasLevelCompleted)
	{
		//disable buttons
		buttonsActive = false;
		statusbarActive = false;
		
		levelTextBox.setText(String.format("%03d",myPattern.size()));
		
		if(wasLevelCompleted)
		{
			this.beginNewLevel();
			return;
		}
		
		//restart the countdown timer... delay execution for 1 sec.
		new Handler().postDelayed(new Runnable() {
			
			@Override
			public void run()
			{
				GameBoard4.this.countdownTimerText.setVisibility(View.VISIBLE);
				GameBoard4.this.myCountdownTimer.restart();
			}
		}, 1000);
	}
	
	/***
	 * this starts the countdown timer at the beginning of levels != 1
	 */
	private void beginNewLevel()
	{
		//disable buttons
		buttonsActive = false;
		statusbarActive = false;
		
		//increase the level number
		final int currentLevel = Integer.parseInt(levelTextBox.getText().toString());
		levelTextBox.setText(String.format("%03d",(currentLevel+1)));
		
		//restart the countdown timer... delay execution for 1 sec.
		new Handler().postDelayed(new Runnable() {
			
			@Override
			public void run()
			{
				GameBoard4.this.countdownTimerText.setVisibility(View.VISIBLE);
				GameBoard4.this.myCountdownTimer.restart();
			}
		}, 1000);
	}

	@Override
	public void onCountdownFinished()
	{
		countdownTimerText.setVisibility(View.GONE);
		
		//start showing the pattern... delay for 500 ms
		new Handler().postDelayed(new Runnable() {
			
			@Override
			public void run()
			{
				GameBoard4.this.showThePattern();
			}
		}, 500);
	}
	
	/***
	 * Starts the display of the lighted button sequence. When finished, the user
	 * is allowed to try and repeat the pattern. 
	 */
	private void showThePattern()
	{
		showingPattern = true;
		if(mySaveGame!=null)
		{
			if(!mySaveGame.getIsLevelComplete())
			{
				//just show the pattern
				myPatternPlayer.playPattern(false);
				mySaveGame = null;//important... so this code block isn't called again
				return;
			}
		}
		
		//add new button to the pattern
		final int nextButton = this.getNextRandomNumber(1, 4);
		//System.out.println(""+nextButton);
		myPatternPlayer.add(nextButton);
		myPatternPlayer.playPattern(false);
	}
	
	/***
	 * starts the portion of the game that allows for user input of the pattern guess
	 */
	public void startGame()
	{
		//make the replayButton visible every 5 turns if it isn't already
		if(Integer.parseInt(levelTextBox.getText().toString())%5 == 1 && replayButton.getVisibility()==View.INVISIBLE && !replayResetUsed)
		{
			replayButton.setVisibility(View.VISIBLE);
			replayResetUsed = true;
		}
		currentPatternIndex = 0;
		//set the buttons active for user input
		buttonsActive = true;
		statusbarActive = true;
		showingPattern = false;
	}
	
	/**
	 * ends the current game and allows the use to go back to the main menu
	 */
	private void gameOver()
	{
		Utilities.deleteSavegame(this);
		Utilities.addHighScore(this, Utilities.GAME_TYPE_4_BUTTON, Integer.parseInt(levelTextBox.getText().toString()));
		buttonsActive = false;
		isGameOver = true;
		failLayout.setVisibility(View.VISIBLE);
		forwardButton.setVisibility(View.VISIBLE);
	}
	
	public boolean isSoundOn()
	{
		return soundFxOn;
	}
	
	/**
	 * Checks the if the user has entered the entire pattern
	 * @return true if the currentPatternIndex == the myPattern.size()... false otherwise
	 */
	private boolean isLevelComplete()
	{
		return (currentPatternIndex == myPattern.size());
	}
	
	/***
	 * The user has completed a level. This shows the appropriate message and sets conditions for the next level.
	 * The program will now wait for the user to touch the => icon to continue.
	 */
	private void levelUp()
	{
		System.gc();//good time to free up resources
		buttonsActive = false;
		successLayout.setVisibility(View.VISIBLE);
		forwardButton.setVisibility(View.VISIBLE);
	}
	
	/***
	 * Returns a random number in the passed integer range
	 * @param min
	 * @param max
	 * @return an int between [min, max] (inclusive)
	 */
	private int getNextRandomNumber(int min, int max)
	{
		return (min + (int)(randomGen.nextDouble() * ((max - min) + 1)));
	}
	
	/***************Section Below Handles Touch/Click/Keypress Events (User Input)*******************/
	@Override
	public boolean onTouch(View v, MotionEvent e)
	{		
		if(e.getAction() == MotionEvent.ACTION_DOWN && buttonsActive)
		{
			//play tones
			if(soundFxOn && (v==redButtonTop || v==yellowButtonTop || v==blueButtonTop || v==greenButtonTop))
			{
				soundCache.get(v).start();
			}
			
			if(v == redButtonTop)
			{
				redButtonLight.setVisibility(View.VISIBLE);
			}
			else if(v == yellowButtonTop)
			{
				yellowButtonLight.setVisibility(View.VISIBLE);
			}
			else if(v == blueButtonTop)
			{
				blueButtonLight.setVisibility(View.VISIBLE);
			}
			else if(v == greenButtonTop)
			{
				greenButtonLight.setVisibility(View.VISIBLE);
			}
			return true;
		}
		else if(e.getAction() == MotionEvent.ACTION_UP && buttonsActive)
		{
			//stop the tone
			if(soundFxOn && (v==redButtonTop || v==yellowButtonTop || v==blueButtonTop || v==greenButtonTop))
			{
				soundCache.get(v).pause();
			}
			
			if(v == redButtonTop)
			{
				redButtonLight.setVisibility(View.INVISIBLE);
				//check against the pattern
				if(this.validateInput(1))
				{
					//check for level end
					if(this.isLevelComplete())
					{
						//level up
						this.levelUp();
					}
				}
				else
				{
					//end the game
					this.gameOver();
				}
			}
			else if(v == yellowButtonTop)
			{
				yellowButtonLight.setVisibility(View.INVISIBLE);
				//check against the pattern
				if(this.validateInput(2))
				{
					//check for level end
					if(this.isLevelComplete())
					{
						//level up
						this.levelUp();
					}
				}
				else
				{
					//end the game
					this.gameOver();
				}
			}
			else if(v == blueButtonTop)
			{
				blueButtonLight.setVisibility(View.INVISIBLE);
				//check against the pattern
				if(this.validateInput(3))
				{
					//check for level end
					if(this.isLevelComplete())
					{
						//level up
						this.levelUp();
					}
				}
				else
				{
					//end the game
					this.gameOver();
				}
			}
			else if(v == greenButtonTop)
			{
				greenButtonLight.setVisibility(View.INVISIBLE);
				//check against the pattern
				if(this.validateInput(4))
				{
					//check for level end
					if(this.isLevelComplete())
					{
						//level up
						this.levelUp();
					}
				}
				else
				{
					//end the game
					this.gameOver();
				}
			}
			return true;
		}
		
		return false;
	}
	
	private boolean validateInput(int buttonPressed)
	{
		
		final int buttonExpected = myPattern.get(currentPatternIndex);
		//System.out.println("Expected: "+buttonExpected+"   Entered: "+buttonPressed);
		if(buttonExpected == buttonPressed)
		{
			currentPatternIndex++;
			return true;
		}
		//else
		return false;
	}
		
	@Override
	public void onClick(View v)
	{
		//check for clicks
		//can use == instead of .equals(...)
		if(v == resumeButton)
		{
			buttonsActive = true;
			statusbarActive = true;
			pauseMenuLayout.setVisibility(View.GONE);
		}
		else if(v==fxOffRadioButton)
		{
			if(fxOffRadioButton.isChecked())
			{
				soundFxOn = false;
			}
		}
		else if(v==fxOnRadioButton)
		{
			if(fxOnRadioButton.isChecked())
			{
				soundFxOn = true;
			}
		}
		else if(forwardButton == v && statusbarActive)
		{
			if(!isGameOver)
			{
				forwardButton.setVisibility(View.INVISIBLE);
				successLayout.setVisibility(View.GONE);
				this.beginNewLevel();
			}
			else
			{
				//return to the main menu
				Intent myIntent = new Intent(this, Main.class);
				//this.startActivityForResult(myIntent, 1);
				this.startActivity(myIntent);
				this.finish();
			}
		}
		else if(v == replayButton && statusbarActive && !showingPattern && 
				this.successLayout.getVisibility()!=View.VISIBLE && this.failLayout.getVisibility()!=View.VISIBLE)
		{
			statusbarActive = false;
			buttonsActive = false;
			myPatternPlayer.playPattern(true);//repeat, so don't decrease delay/display times
			replayButton.setVisibility(View.INVISIBLE);
		}
		else if(v == pauseButton && statusbarActive)
		{
			buttonsActive = false;
			statusbarActive = false;
			pauseMenuLayout.startAnimation(animationCache[1]);
			if(soundFxOn)
			{
				fxOnRadioButton.setChecked(true);
			}
			else
			{
				fxOffRadioButton.setChecked(true);
			}
			pauseMenuLayout.setVisibility(View.VISIBLE);
		}
	}
	
	//intercept the back and menu key presses
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		if(keyCode == KeyEvent.KEYCODE_BACK)
		{
			if(pauseMenuLayout.getVisibility() == View.VISIBLE)
			{
				resumeButton.performClick();
				return true;
			}
		}
		else if(keyCode == KeyEvent.KEYCODE_MENU && buttonsActive)
		{
			pauseButton.performClick();
			return true;
		}
		//else
		return super.onKeyDown(keyCode, event);
	}
	/********************************************************************************************/
	
	@Override
	protected void onPause()
	{
		//System.out.println("onPause");
		if(!this.isGameOver)
		{
			Utilities.saveCurrentGame(this, Utilities.GAME_TYPE_4_BUTTON, myPattern, this.isLevelComplete());
		}
		
		//stop any playing sounds in the patternplayer
		myPatternPlayer.cancelPlayback();
		
		//release resources for the mediaplayers
		try
		{
			for(MediaPlayer mp : soundCache.values())
			{
				if(mp.isPlaying())
				{
					mp.stop();
				}
				
				mp.release();
			}
		}
		catch (Exception e)
		{
			//nothing
		}
		
		super.onPause();
		
		//close the app
		this.finish();
	}
	
	@Override
	protected void onResume()
	{
		//System.out.println("onResume");
		mySaveGame = Utilities.loadSavegame(this);
		
		//check the savegame
        if(mySaveGame!=null)
		{
			myPatternPlayer.setPattern(mySaveGame.getPattern());
			myPattern = myPatternPlayer.getPattern();
			
			this.startFromSavedGame(mySaveGame.getIsLevelComplete());
			
		}
        else
        {
        	//run the 1st-run components
            this.startFirstRun();
        }
        
		super.onResume();
	}
}
